package com.bbf.mathgen.service;

import com.bbf.mathgen.util.GenUtil;
import com.bbf.mathgen.util.WordGeneratorUtil;
import freemarker.template.Configuration;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数学题文件生成
 * <p>加减法</p>
 * @author BBF
 */
public class MathService {
  /**
   * 默认算术的最小值
   */
  private static final int MATH_MIN = 1;

  /**
   * 默认算术的最大值
   */
  private static final int MATH_MAX = 20;

  /**
   * 口算题的算式长度加权区间，可以提高某一类的碰撞强度
   */
  private static final int[] ARITHMETIC_SECTION = {2, 2, 3, 3, 3, 3, 3, 3, 3, 3};

  /**
   * 比较大小题的算式长度
   */
  private static final int[] COMPARISON_SECTION = {2, 2, 2, 2, 2, 2, 2, 2, 3, 3};

  /**
   * 支持的算术符号
   */
  private final MathEnum[] SINGE = {MathEnum.plus, MathEnum.minus};

  /**
   * 随机运算的符号
   * @return 随机运算的符号
   */
  private MathEnum getMathEnum() {
    return MathEnum.getByCode(GenUtil.nextInt(0, SINGE.length));
  }

  /**
   * 随机运算的数字
   * @return 随机运算的数字
   */
  private int getRandomNumber() {
    return GenUtil.nextInt(MATH_MIN, MATH_MAX);
  }

  /**
   * 检查是否在合理区间
   * @param dto 算式对象
   * @return true - 合理
   */
  private boolean checkDto(MathDto dto) {
    //剔除不合格的数值，[MATH_MIN,MATH_MAX]之外的
    int ct = dto.getResult();
    return MATH_MIN <= ct && MATH_MAX >= ct;
  }

  /**
   * 获取一条计算题
   * @param section 计算量的碰撞区间
   * @return 计算题，如果不符合规则返回空字符串
   */
  private String getOneFormula(int[] section) {
    MathDto result = new MathDto(getRandomNumber(), getRandomNumber(), getMathEnum());
    if (checkDto(result)) {
      // 本次算式的数字数量，减去2，因为在前面已经有两组数字计算了
      int fx = section[GenUtil.nextInt(0, section.length)] - 2;
      if (fx < 0) {
        fx = 0;
      }
      for (int i = 0; i < fx; i++) {
        result = new MathDto(result, getRandomNumber(), getMathEnum());
        if (!checkDto(result)) {
          break;
        }
      }
    }
    if (checkDto(result)) {
      return result.getFormula();
    }
    return "";
  }

  /**
   * 获取指定数量的口算题集合
   * @param mathCount 口算题数量
   * @return 口算题集合
   */
  private List<String> getArithmeticList(int mathCount) {
    List<String> list = new ArrayList<>(mathCount);
    int idx = 0;
    while (mathCount > idx) {
      String mathStr = getOneFormula(ARITHMETIC_SECTION);
      if (mathStr.length() > 0) {
        list.add(mathStr + " =");
        idx++;
      }
    }
    return list;
  }

  /**
   * 获取指定数量的比较大小题集合
   * @param mathCount 比较大小题数量
   * @return 口算题集合
   */
  private List<String> getComparisonList(int mathCount) {
    List<String> list = new ArrayList<>(mathCount);
    int idx = 0;
    while (mathCount > idx) {
      String mathStr1 = getOneFormula(COMPARISON_SECTION);
      String mathStr2 = getOneFormula(COMPARISON_SECTION);
      if (mathStr1.length() > 0 && mathStr2.length() > 0) {
        list.add(mathStr1 + " (     ) " + mathStr2);
        idx++;
      }
    }
    return list;
  }

  /**
   * 生成doc文件
   * @param data Map数据
   * @param fileName 生成的文件名
   * @return 成功返回文件全名，失败抛异常
   * @throws Exception 生成文件异常
   */
  private String createDoc(Map<String, Object> data, String fileName) throws Exception {
    Configuration configuration = new Configuration(Configuration.VERSION_2_3_23);
    //模板文件放在resource根目录，可以自行调整
    configuration.setClassForTemplateLoading(WordGeneratorUtil.class, "/docs");
    File file = WordGeneratorUtil.createDoc(configuration, data, "mathTemplate.xml", fileName);
    return file.getAbsolutePath();
  }

  /**
   * 创建口算文件
   * @param arithmeticCount 口算题目数量
   * @param comparisonCount 比较大小题目数量
   * @param fileName 文件名
   * @param userName 答题人
   * @return 成功返回文件全名，失败抛异常
   * @throws Exception 生成文件异常
   */
  public String createMathFile(int arithmeticCount,
                               int comparisonCount,
                               String fileName,
                               String userName) throws Exception {
    return this.createMathFile(arithmeticCount, comparisonCount, fileName, userName, 1);
  }

  /**
   * 创建口算文件
   * @param arithmeticCount 口算题目数量
   * @param comparisonCount 比较大小题目数量
   * @param fileName 文件名
   * @param userName 答题人
   * @param ct 题目页数
   * @return 成功返回文件全名，失败抛异常
   * @throws Exception 生成文件异常
   */
  public String createMathFile(int arithmeticCount,
                               int comparisonCount,
                               String fileName,
                               String userName,
                               int ct) throws Exception {
    List<Map<String, Object>> mathList = new ArrayList<>(ct);
    String name = userName != null ? userName : "";
    for (int i = 0; i < ct; i++) {
      List<String> arithmeticList = getArithmeticList(arithmeticCount);
      List<String> comparisonList = getComparisonList(comparisonCount);
      //文件内容
      Map<String, Object> data = new HashMap<>(4);
      data.put("arithmeticCount", arithmeticList.size() + "道");
      data.put("arithmeticList", arithmeticList);
      data.put("comparisonCount", comparisonList.size() + "道");
      data.put("comparisonList", comparisonList);
      mathList.add(data);
    }
    Map<String, Object> allData = new HashMap<>(2);
    allData.put("username", name);
    allData.put("allDataList", mathList);
    return createDoc(allData, fileName);
  }

}
